#include "GeometryData.h"

using namespace Nezha;


GeometryData::GeometryData()
	:mGeometryType(TRIANGLE_LIST)
{

}

GeometryData::GeometryData(GeometryType gt)
	:mGeometryType(gt)
{

}

GeometryData::~GeometryData()
{
	mIndexBufferPtr = NULL;
	for(int i = 0; i < NEZHA_MAX_VERTEX_STREAMS; i++)
	{
		mVertexBufferPtrs[i] = NULL;
	}
}

void GeometryData::setVertexStream(u32 index, VertexBufferPtr& vb)
{
	NZ_ASSERT(index < NEZHA_MAX_VERTEX_STREAMS);

	mVertexBufferPtrs[index] = vb;
}

const VertexBufferPtr& GeometryData::getVertexBuffer(u32 index) const
{
	NZ_ASSERT(index < NEZHA_MAX_VERTEX_STREAMS);

	return mVertexBufferPtrs[index];
}

u32 GeometryData::getNumVertex() const
{
	u32 numVertsOfBuffer = 0;

	for(int i = 0; i < NEZHA_MAX_VERTEX_STREAMS; i++)
	{
		if(mVertexBufferPtrs[i].ptr())
		{
			numVertsOfBuffer = mVertexBufferPtrs[i]->getNumVertex();
			break;
		}
	}

	return numVertsOfBuffer;
}


// geometry data batch
GeometryDataBatch::GeometryDataBatch()
	:mNumVerts(0)
	,mNumIdxs(0)
	,mNumVertOffset(0)
	,mNumIdxOffset(0)
{
}

bool GeometryDataBatch::createBatch(u32 numVertsOffset, u32 numIdxOffset, u32 numVerts, u32 numIdxs, const GeometryDataPtr& data)
{
	if(!data)
		return false;

	u32 numVertsOfBuffer = data->getNumVertex();

	if(numVertsOfBuffer == 0 || numVerts + numVertsOffset > numVertsOfBuffer)
	{
		return false;
	}

	if(data->getIndexBuffer().notNull())
	{
		if(numIdxs + numIdxOffset > data->getIndexBuffer()->getNumIndices())
		{
			return false;
		}
	}

	mGeomDataBuffer = data;
	mNumVertOffset = numVertsOffset;
	mNumIdxOffset = numIdxOffset;
	mNumVerts = numVerts;
	mNumIdxs = numIdxs;

	return true;
}

GeometryDataBatch& GeometryDataBatch::operator = (const GeometryDataBatch& gdb)
{
	mNumVerts = gdb.mNumVerts;
	mNumVertOffset = gdb.mNumVertOffset;
	mNumIdxs = gdb.mNumIdxs;
	mNumIdxOffset = gdb.mNumIdxOffset;
	mGeomDataBuffer = gdb.mGeomDataBuffer;

	return *this;
}

void GeometryDataBatch::clear()
{
	mNumVerts = mNumIdxs = 0;
	mNumVertOffset = mNumIdxOffset = 0;
	mGeomDataBuffer = NULL;
}




VertexBufferAccessor::VertexBufferAccessor()
	:mIndices(NULL)
	,mAccessProcessing(false)
{

}

VertexBufferAccessor::~VertexBufferAccessor()
{
	if(mAccessProcessing)
	{
		endAccess();
	}
}

const VertexBufferAccessor::VertexElemBinding* VertexBufferAccessor::getElemBindingByVS(VertexSementic vs, u32 index) const
{
	for(PArray<VertexElemBinding>::const_iterator it = mBindings.begin(); it != mBindings.end(); it++)
	{
		if((*it).Semantic == vs)
		{
			return &(*it);
		}
	}

	return NULL;
}

bool VertexBufferAccessor::beginAccess(GeometryDataPtr& gd)
{
	mBindings.clear();

	if(gd->getIndexBuffer().ptr() && gd->getIndexBuffer()->isLocked())
	{
		NEZHA_EXCEPTION(NZException::InvalidState, "Index buffer already locked!!");
		return false;
	}

	IndexBufferPtr ib = gd->getIndexBuffer();
	int* indices = (int*)ib->lock(HardwareBuffer::HBL_READ_WRITE);

	VertexElemBinding veb;
	VertexElemBinding VBDatas[NEZHA_MAX_VERTEX_STREAMS];

	for(u32 i = 0; i < NEZHA_MAX_VERTEX_STREAMS; i++)
	{
		VertexBufferPtr vb = gd->getVertexBuffer(i);
		if(vb.notNull())
		{
			if(vb->isLocked())
			{
				NEZHA_EXCEPTION(NZException::InvalidState, "Vertex buffer already locked!!");
				return false;
			}

			VBDatas[i].Data = (float*)vb->lock(HardwareBuffer::HBL_READ_WRITE);
			VBDatas[i].StreamIndex = i;
			VBDatas[i].BufferStride = vb->getVertexStride();
		}
	}

	u32 numVE = gd->getVertexDecl().numElements();
	for(u32 i = 0; i < numVE; i++)
	{
		const VertexElement* ve = gd->getVertexDecl().getElement(i);
		if(!ve)
		{
			break;
		}

		veb.StreamIndex = ve->StreamIndex;
		veb.BufferStride = VBDatas[veb.StreamIndex].BufferStride;
		veb.Data = (float*)((char*)VBDatas[veb.StreamIndex].Data + ve->Offset);
		veb.Semantic = ve->Sementic;

		mBindings.push_back(veb);
	}

	mBindingGD = gd;
	mIndices = indices;
	mAccessProcessing = !mBindings.empty();

	return mAccessProcessing;
}

void VertexBufferAccessor::endAccess()
{
	if(!mBindingGD)
	{
		mAccessProcessing = false;
		return;
	}

	for(u32 i = 0; i < NEZHA_MAX_VERTEX_STREAMS; i++)
	{
		VertexBufferPtr vb = mBindingGD->getVertexBuffer(i);
		if(vb.notNull())
		{
			vb->unlock();
		}
	}

	if(mBindingGD->getIndexBuffer().ptr())
	{
		IndexBufferPtr ib = mBindingGD->getIndexBuffer();
		ib->unlock();
	}

	mBindingGD = NULL;
	mIndices = NULL;
	mBindings.clear();

	mAccessProcessing = false;
}